// -*- indent-tabs-mode: nil -*-
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace seanfoy.oopsnet {
    public class RelatedBugsControl : WebControl {
        public RelatedBugsControl() : base("div") {}
        private string xsltUrlField;
        /// <summary>
        /// A link to
        /// Extensible Stylesheet Language Transformations
        /// for the presentation of bug query results
        /// </summary>
        public string xsltUrl {
            get {
                return xsltUrlField;
            }
            set {
                xsltUrlField = value;
            }
        }
        private string xsltFileField;
        /// <summary>
        /// A filesystem path to
        /// Extensible Stylesheet Language Transformations
        /// for the presentation of bug query results
        /// </summary>
        public string xsltFile {
            get {
                return xsltFileField;
            }
            set {
                xsltFileField = value;
            }
        }
        private string apologyField;
        public string apology {
            get {
                return apologyField;
            }
            set {
                apologyField = value;
            }
        }
        private Exception exceptionField;
        /// <summary>
        /// The primary problem
        /// </summary>
        public Exception exception {
            get {
                return exceptionField;
            }
            set {
                exceptionField = value;
            }
        }
        private Uri requestUrlField;
        public Uri requestUrl {
            get {
                return requestUrlField;
            }
            set {
                requestUrlField = value;
            }
        }
        private Exception troubleField;
        /// <summary>
        /// Trouble with Bugzilla, secondary to
        /// <see cref="exception" />.
        /// </summary>
        public Exception trouble {
            get {
                return troubleField;
            }
            set {
                troubleField = value;
            }
        }
        protected override void Render(HtmlTextWriter writer) {
            try {
                string xsltSource = xsltUrl;
                if (xsltSource == null) {
                    xsltSource =
                        Path.Combine(
                            Context.Server.MapPath(TemplateSourceDirectory),
                            xsltFile);
                }
                writer.Write(fetchRelatedBugs(xsltSource));
            }
            catch (Exception e) {
                trouble = e;
                apologize();
            }
            catch {
                apologize();
            }
            base.Render(writer);
        }

        public IEnumerable makeCNF() {
            return
                Bugzilla.makeCNF(
                    exception,
                    requestUrl,
                    ASPNetErrorHandler.AppSettings);
        }

        /// <exception type="System.Net.WebException">thrown
        /// when the Bugzilla server can't be reached</exception>
        protected string fetchRelatedBugs(string xsltUrl) {
            if (exception == null) {
                throw new InvalidOperationException("The exception property must be set before this control can be rendered.");
            }

            xsltUrl = new Uri(Context.Request.Url, ResolveUrl(xsltUrl)).ToString();

            String buglistBaseUrl =
                ASPNetErrorHandler.AppSettings["seanfoy.oopsnet.bzQueryUrl"];
            if (Potpourri.coalesce(buglistBaseUrl, String.Empty) == String.Empty) {
                throw new Exception("This control requires a non-empty AppSetting with key: seanfoy.oopsnet.bzQueryUrl");
            }

            IDictionary queryArgs =
                Bugzilla.queryargs(makeCNF());
            IDictionary htmlQueryArgs = new ListDictionary();
            foreach (DictionaryEntry i in queryArgs) {
                htmlQueryArgs[i.Key] = i.Value;
            }
            htmlQueryArgs.Remove("ctype");

            String bugSource =
                String.Format(
                    "{0}?{1}",
                    buglistBaseUrl,
                    Bugzilla.querystring(queryArgs));
            String htmlBugSource =
                String.Format(
                    "{0}?{1}",
                    buglistBaseUrl,
                    Bugzilla.querystring(htmlQueryArgs));
            try {
                WebRequest req = WebRequest.Create(bugSource);
                using (WebResponse resp = req.GetResponse()) {
                    XslTransform bugsToHtml = new XslTransform();
                    bugsToHtml.Load(xsltUrl);
                    using (Stream s = resp.GetResponseStream()) {
                        XPathDocument bugs = new XPathDocument(s);
                        XsltArgumentList parameters = new XsltArgumentList();
                        parameters.AddParam("htmlUrl", String.Empty, htmlBugSource);
                        XmlReader r = bugsToHtml.Transform(bugs, parameters, new XmlUrlResolver());
                        StringBuilder sb = new StringBuilder();
                        while (!r.EOF) {
                            sb.Append(r.ReadOuterXml());
                            sb.Append("\n");
                            r.MoveToContent();
                        }
                        return sb.ToString();
                    }
                }
            }
            catch (WebException e) {
                string url = bugSource;
                if (e.Response != null && e.Response.ResponseUri != null) {
                    url = e.Response.ResponseUri.ToString();
                }
                throw new WebException(
                    String.Format(
                        "Could not retrieve {0}",
                        url), e);
            }
        }

        protected void apologize() {
            if (apology != null) {
                Controls.Add(Page.LoadControl(apology));
                return;
            }
            //failsafe
            try {
                Literal l = new Literal();
                l.Text =
                    String.Format(
                        "<p>Incidentally, we tried and failed to find relevant bugs in Bugzilla. Here are some specific details about that problem: {0}</p>",
                        Oops.htmlize(trouble));
                Controls.Add(l);
            }
            catch {
                //don't interfere with
                //the display of info
                //about the primary
                //error.
            }
        }
    }
}
